/**
 * 格式工具
 * @author luguoxiang
 * @date 2021-09-07
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
import { sizeFormat } from './file';

const REGEXP_FORMAT_STR = /\${([^{}]+)}/g;
/**
 * 截取字串
 * @param s
 * @param f
 * @param l
 * @returns {string}
 */
function sn(s: string, f: number, l: number): any {
  return s.substring(f, f + l);
}

/**
 * 日期解析，将各种日期数据类型转为 Date 类型
 * @param {Object} date
 */
export function dateParse(date: string|number|Date|number[]): Date {
  if (date instanceof Date) {
    return date;
  }
  if (typeof date === 'string') {
    // @ts-ignore
    date = date.split(/[^0-9]+/g);
    date = (date as any[])
      .filter((x) => x)
      .map((x) => (x.length === 1 ? `0${x}` : x))
      .join('');
    // @ts-ignore 只有年
    if (date.length === 4) {
      // @ts-ignore
      return new Date(date, 1);
    }
    // @ts-ignore 年月
    if (date.length === 6) {
      // @ts-ignore
      return new Date(sn(date, 0, 4), sn(date, 4, 2) - 1);
    }
    // @ts-ignore 年月日
    if (date.length === 8) {
      // @ts-ignore
      return new Date(sn(date, 0, 4), sn(date, 4, 2) - 1, sn(date, 6, 2));
    }
    // @ts-ignore 年月日时
    if (date.length === 10) {
      return new Date(
        // @ts-ignore
        sn(date, 0, 4),
        sn(date, 4, 2) - 1,
        sn(date, 6, 2),
        // @ts-ignore
        sn(date, 8, 2),
      );
    }
    // @ts-ignore 年月日时分
    if (date.length === 12) {
      return new Date(
        // @ts-ignore
        sn(date, 0, 4),
        sn(date, 4, 2) - 1,
        sn(date, 6, 2),
        // @ts-ignore
        sn(date, 8, 2),
        sn(date, 10, 2),
      );
    }
    // @ts-ignore 年月日时分秒
    if (date.length === 14) {
      return new Date(
        // @ts-ignore
        sn(date, 0, 4),
        sn(date, 4, 2) - 1,
        sn(date, 6, 2),
        // @ts-ignore
        sn(date, 8, 2),
        sn(date, 10, 2),
        sn(date, 12, 2),
      );
    }
    // 年月日时分毫秒
    return new Date(
      // @ts-ignore
      sn(date, 0, 4),
      sn(date, 4, 2) - 1,
      sn(date, 6, 2),
      // @ts-ignore
      sn(date, 8, 2),
      sn(date, 10, 2),
      sn(date, 12, 2),
      // @ts-ignore
      sn(date, 14, 3),
    );
  }
  if (Array.isArray(date)) {
    // @ts-ignore
    return new Date(...date);
  }
  return new Date(date);
}

type DateFormatter = (date: Date) => string;
/**
 * 创建日期格式化器
 * @param fmt 格式
 */
export function createDateFormatter(fmt: string): DateFormatter {
  let variable = `var d = arguments[0]; `;
  fmt = `"${fmt.replaceAll('"', '\\"').replaceAll("'", "\\'")}`;
  // 年
  if (fmt.includes('YYYY')) {
    variable += 'var _00=d.getFullYear(); ';
    fmt = fmt.replace('YYYY', '"+ _00 +"');
  }
  if (fmt.includes('YY')) {
    variable += 'var _01=d.getFullYear() % 100; ';
    fmt = fmt.replace('YY', '"+ _01 +"');
  }
  // 月
  if (fmt.includes('MM')) {
    variable += 'var _10=d.getMonth() + 1; if(_10<10){_10 = "0"+_10;} ';
    fmt = fmt.replace('MM', '"+ _10 +"');
  }
  if (fmt.includes('M')) {
    variable += 'var _11=d.getMonth() + 1; ';
    fmt = fmt.replace('M', '"+ _11 +"');
  }
  // 日
  if (fmt.includes('DD')) {
    variable += 'var _20=d.getDate(); if(_20<10){_20 = "0"+_20;} ';
    fmt = fmt.replace('DD', '"+ _20 +"');
  }
  if (fmt.includes('D')) {
    variable += 'var _21=d.getDate(); ';
    fmt = fmt.replace('D', '"+ _21 +"');
  }
  // 时
  if (fmt.includes('hh')) {
    variable += 'var _30=d.getHours(); if(_30<10){_30 = "0"+_30;} ';
    fmt = fmt.replace('hh', '"+ _30 +"');
  }
  if (fmt.includes('h')) {
    variable += 'var _31=d.getHours(); ';
    fmt = fmt.replace('h', '"+ _31 +"');
  }
  // 分
  if (fmt.includes('mm')) {
    variable += 'var _40=d.getMinutes(); if(_40<10){_40 = "0"+_40;} ';
    fmt = fmt.replace('mm', '"+ _40 +"');
  }
  if (fmt.includes('m')) {
    variable += 'var _41=d.getMinutes(); ';
    fmt = fmt.replace('m', '"+ _41 +"');
  }
  // 秒
  if (fmt.includes('ss')) {
    variable += 'var _50=d.getSeconds(); if(_50<10){_50 = "0"+_50;} ';
    fmt = fmt.replace('ss', '"+ _50 +"');
  }
  if (fmt.includes('s')) {
    variable += 'var _51=d.getSeconds(); ';
    fmt = fmt.replace('s', '"+ _51 +"');
  }
  // 毫秒
  if (fmt.includes('Z')) {
    variable += 'var _60=d.getMilliseconds(); ';
    fmt = fmt.replace('Z', '"+ _60 +"');
  }
  // 星期
  if (fmt.includes('W')) {
    variable += "var _70='日一二三四五六'[d.getDay()]; ";
    fmt = fmt.replace('W', '"+ _70 +"');
  }
  if (fmt.includes('w')) {
    variable += "var _71=['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][d.getDay()]; ";
    fmt = fmt.replace('w', '"+ _71 +"');
  }
  // 季度
  if (fmt.includes('Q')) {
    variable += "var _80='一二三四'[Math.floor(d.getMonth() / 3)]; ";
    fmt = fmt.replace('Q', '"+ _80 +"');
  }
  if (fmt.includes('q')) {
    variable += "var _81='1234'[Math.floor(d.getMonth() / 3)]; ";
    fmt = fmt.replace('q', '"+ _81 +"');
  }
  fmt += '"';
  fmt = fmt.replace('+""+', '');
  if (fmt.startsWith('""+')) {
    fmt = fmt.substring(3);
  }
  if (fmt.endsWith('+""')) {
    fmt = fmt.substring(0, fmt.length - 3);
  }
  // @ts-ignore
  // eslint-disable-next-line no-new-func
  return new Function(`${variable}return ${fmt}`);
}

/**
 * 格式器缓存
 */
const dateFormatter: { [k: string]: DateFormatter } = {};
/**
 * 日期格式化，将各种日期数据类型转为指定字符串格式
 * @param {Object} date
 * @param {string} fmt
 */
export function dateFormat(date: Date | string | number | string[] | number[], fmt = 'YYYY-MM-DD hh:mm'): string {
  if (!date) {
    return '';
  }
  // @ts-ignore
  date = dateParse(date);
  let formatter = dateFormatter[fmt];
  if (!formatter) {
    formatter = createDateFormatter(fmt);
    dateFormatter[fmt] = formatter;
  }
  return formatter(date);
}

/**
 * 数值 toFixed
 * @param value
 * @param precision
 */
export function toFixed(value: number, precision = 0) {
  // eslint-disable-next-line no-restricted-globals
  if (value === null || isNaN(value)) {
    return value;
  }
  // @ts-ignore
  return parseFloat(value).toLocaleString(undefined, {
    useGrouping: false,
    minimumFractionDigits: precision,
    maximumFractionDigits: precision,
  });
}

/**
 * 数值格式
 * @param value
 * @param options
 */
export function numberFormat(value: number, options: Intl.NumberFormatOptions | number = 0) {
  // eslint-disable-next-line no-restricted-globals
  if (value === null || isNaN(value)) {
    return value;
  }
  if (typeof options === 'number') {
    options = { maximumFractionDigits: options, minimumFractionDigits: options };
  }
  // @ts-ignore
  return parseFloat(value).toLocaleString(undefined, options);
}

/**
 * 时长格式化
 * @param seconds 秒
 */
export function durationFormat(seconds: number): string {
  let format = '';
  if (seconds < 0) {
    format = '-';
    seconds = -seconds;
  }
  if (!seconds || seconds < 1) {
    return '0秒';
  }
  const day = Math.floor(seconds / 86400);
  seconds %= 86400;
  const hour = Math.floor(seconds / 3600);
  seconds %= 3600;
  const minutes = Math.floor(seconds / 60);
  seconds %= 60;
  const ds = [day, hour, minutes, seconds];
  const ts = ['天', '小时', '分钟', '秒'];
  let flag = true;
  for (let i = 0; i < ds.length; i++) {
    const x = ds[i];
    if (flag) {
      if (x === 0) {
        continue;
      }
      flag = false;
    }
    format += x + ts[i];
  }
  return format;
}

/**
 * ip转int
 * @param {String} ip
 * @returns {Number}
 */
export function ipToInt(ip: string): number {
  if (!ip || ip.length === 0) {
    // @ts-ignore
    return null;
  }
  // @ts-ignore
  ip = ip.split('.');
  // @ts-ignore
  // eslint-disable-next-line no-bitwise
  ip = (parseFloat(ip[0]) << 24) + (parseFloat(ip[1]) << 16) + (parseFloat(ip[2]) << 8) + parseFloat(ip[3]);
  // @ts-ignore
  return ip;
}

/**
 * int转ip
 * @param {Number} num
 * @returns {{String}}
 */
export function intToIp(num: number): string {
  const arr = [];
  arr[0] = (num >>> 24) >>> 0;
  arr[1] = ((num << 8) >>> 24) >>> 0;
  arr[2] = (num << 16) >>> 24;
  arr[3] = (num << 24) >>> 24;
  return `${arr[0]}.${arr[1]}.${arr[2]}.${arr[3]}`;
}

/**
 * 字符串格式（自动替换 ${field} 为 params[ field ] 的内容）
 * @param pattern
 * @param params
 * @param defValue
 */
export function formatStr(pattern: string, params: object, defValue?: any): string {
  if (pattern in params) {
    // @ts-ignore
    return params[pattern];
  }
  const match = pattern.match(REGEXP_FORMAT_STR);
  if (match) {
    const map: { [p: string]: any } = {};
    for (const x of match) {
      if (map[x]) {
        continue;
      }
      map[x] = true;
      const key = x.substring(2, x.length - 1);
      // @ts-ignore
      pattern = pattern.replace(x, params[key]);
    }
  }
  return defValue ?? pattern;
}

/**
 * 获取模板key
 * @param {string}pattern
 * @returns {string[]}
 */
formatStr.getPatternKeys = function (pattern: string): string[] {
  const match = pattern.match(REGEXP_FORMAT_STR);
  if (match) {
    const map: { [p: string]: any } = {};
    for (const x of match) {
      if (map[x]) {
        continue;
      }
      map[x] = x.substring(2, x.length - 1);
    }
    return Object.values(map);
  }
  return [pattern];
};

/**
 * 大数值格式化
 * @param value
 */
export function bigintFormat(value: number) {
  if (value || value === 0) {
    value = Math.round(value);
    const len = String(value < 0 ? -value : value).length;
    switch (len) {
      case 5:
        return `${(value / 10000).toFixed(3)}万`;
      case 6:
        return `${(value / 10000).toFixed(2)}万`;
      case 7:
        return `${(value / 10000).toFixed(1)}万`;
      case 8:
        return `${(value / 10000).toFixed(0)}万`;
      case 9:
        return `${(value / 100000000).toFixed(3)}亿`;
      case 10:
        return `${(value / 100000000).toFixed(2)}亿`;
      case 11:
        return `${(value / 100000000).toFixed(1)}亿`;
      case 12:
        return `${(value / 100000000).toFixed(0)}亿`;
      case 13:
        return `${(value / 1000000000000).toFixed(2)}万亿`;
      case 14:
        return `${(value / 1000000000000).toFixed(1)}万亿`;
      case 15:
        return `${(value / 1000000000000).toFixed(0)}万亿`;
      case 16:
        return `${(value / 10000000000000000).toFixed(4)}兆`;
      case 17:
        return `${(value / 10000000000000000).toFixed(3)}兆`;
      case 18:
        return `${(value / 10000000000000000).toFixed(2)}兆`;
      case 19:
        return `${(value / 10000000000000000).toFixed(1)}兆`;
      case 20:
        return `${(value / 10000000000000000).toFixed(0)}兆`;
      default:
        return value;
    }
  }
  return value;
}

type JsonFormatOptions = {
  padding?: string;
  wrap?: string;
};

/**
 * json格式化
 * @param json
 * @param options
 */
export function jsonFormat(json: any, options?: JsonFormatOptions) {
  const { padding = '\t', wrap = '\r' } = options ?? {};
  function fmt(data: { [x: string]: any }, pad: string) {
    switch (typeof data) {
      case 'boolean':
      case 'number':
        return String(data);
      case 'string':
        return `"${data}"`;
      default:
        break;
    }
    const pad1 = pad + padding;
    if (Array.isArray(data)) {
      let str = `[${wrap}`;
      for (const k in data) {
        str += `${pad1}${fmt(data[k], pad1)},${wrap}`;
      }
      str = str.substring(0, str.length - wrap.length - 1) + wrap;
      str += `${pad}]`;
      return str;
    }
    let str = `{${wrap}`;
    for (const k in data) {
      str += `${pad1}${k}: ${fmt(data[k], pad1)},${wrap}`;
    }
    str = str.substring(0, str.length - wrap.length - 1) + wrap;
    str += `${pad}}`;
    return str;
  }
  return fmt(json, '');
}

/**
 * 下划线转驼峰
 * @param name
 */
export function toHump(name: string) {
  return name.replace(/_([a-z])/g, (match, letter) => letter.toUpperCase());
}

type FMT =
  | 'bigint'
  | 'number'
  | 'datetime'
  | 'date'
  | 'time'
  | 'CNY'
  | 'USD'
  | 'EUR'
  | 'file'
  | 'percent'
  | 'json'
  | string
  | number;
/**
 * 智能格式化
 * @param value 值
 * @param fmt 格式
 */
export function format(value: any, fmt?: FMT) {
  switch (fmt) {
    case 'datetime':
      return dateFormat(value);
    case 'date':
      return dateFormat(value, 'YYYY-MM-DD');
    case 'time':
      return dateFormat(value, 'hh:mm');
    case 'CNY':
      return value.toLocaleString('zh-CN', { style: 'currency', currency: 'CNY' });
    case 'USD':
      return value.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
    case 'EUR':
      return value.toLocaleString('en-GB', { style: 'currency', currency: 'EUR' });
    case 'number':
      return value.toLocaleString();
    case 'bigint':
      return bigintFormat(value);
    case 'file':
      return sizeFormat(value);
    case 'percent':
      return `${(value * 100).toPrecision(3)}%`;
    case 'json':
      return jsonFormat(value);
    default:
      if (typeof fmt === 'string') {
        return dateFormat(value, fmt);
      }
      if (typeof fmt === 'number') {
        return value.toPrecision(fmt);
      }
      if (typeof value === 'number') {
        return value.toLocaleString();
      }
      // if (value instanceof File) {
      //   return sizeFormat(value.size);
      // }
      return dateFormat(value);
  }
}
export default format;
